/**
 * 
 */
package com.jiang.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;

import com.jiang.factory.CreateAutoBeanFactory;
import com.jiang.factory.SmartFactory;
import com.jiang.utils.BeanOperatorUtils;
import com.jiang.utils.ValidationUtils;
import com.jspsmart.upload.SmartRequest;
import com.jspsmart.upload.SmartUpload;
import com.jspsmart.upload.SmartUploadException;

/**
 * @author Administrator
 * @通用的父Servlet类
 */
public abstract class DispatcherServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;
	// 因为servlet/jsp 内置对象是线程安全的(除了application/servletContext) 所以可以直接设置为全局变量
	private HttpServletRequest request;
	private HttpServletResponse response;
	
	// 因为对资源文件的读取都是只读，不存在线程安全，所以也可以定义为全局变量
	private ResourceBundle msgBundle; // 读消息
	private ResourceBundle pageBundle; // 读路由
	private ResourceBundle validateBundle; // 读验证

	
	@Override
	public void init() throws ServletException {
		msgBundle = ResourceBundle.getBundle("Message", Locale.getDefault());
		pageBundle = ResourceBundle.getBundle("PageUrl", Locale.getDefault());
		validateBundle = ResourceBundle.getBundle("Validate", Locale.getDefault()); // 读验证
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	/**
	 * 统一的请求处理
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		this.request = req;
		this.response = resp;
		// 初始化默认出错页面
		String defaultPage = "errors.page";
		Map<String, String> errors =  new HashMap<>();
		try {
			// 自动数据封装前进行servlet的vo参数类的验证 
			Map<String,Object> validateResult = ValidationUtils.validate(this);
			if((boolean)validateResult.get("result")){
				// 这里还要进行数据自动封装，前端传来的数据要有规范: member.mid , admin.aid
				autoPreAssemble();
				String invokeMethodName = this.getInvokeMethodName();
				// 利用反射调用相应的servlet中的方法
				Method method = this.getClass().getDeclaredMethod(invokeMethodName);
				defaultPage = (String) method.invoke(this);
			}else{
				// 验证不通过
				errors = (Map<String, String>) validateResult.get("errors");
//				String url= request.getHeader("Referer");
//		         String refer=url.substring(url.indexOf(request.getContextPath()));
				// 这里不用考虑表单封装，表单封装只是限制parameter而不是限制attribute
				this.request.setAttribute("errors", errors);
			}

		} catch (Exception e) {
			e.printStackTrace();
			errors.put("error", this.getMsg("system.error.msg"));
			this.request.setAttribute("errors", errors);
		} finally {
			// 完成了当前的使命，记得清掉ThreadLocal
			CreateAutoBeanFactory.clear();
			// 直接跳转到相应页面
			this.request.getRequestDispatcher(this.getPageUrl(defaultPage)).forward(request, response);
		}

	}

	// 对前端传来的属性进行自动实体封装
	@SuppressWarnings("unchecked")
	public void autoPreAssemble() {

		try {
			Field field = this.getClass().getDeclaredField("vo"); // 获取每个子类中的实例对象vo
			field.setAccessible(true);
			// 获取类型
			Class<?> voclz = field.getType();
			// 利用给定的类名生成对象，然后进行自动的赋值，之后在子类中利用工具类去调用获取需要的对象
			Object currentObj = CreateAutoBeanFactory.getBean(voclz);
			// 这里还要进行数据自动封装，前端传来的数据要有规范: member.mid , admin.aid
			// 根据前端是否传参数来决定之后怎么弄，但是不管怎么样都要在每个servlet中存在一个要操作的对象的class供整个模块使用
			if (isMuitiPart()) {
				SmartUpload smartUpload = SmartFactory.getInstance();
				smartUpload.initialize(super.getServletConfig(), this.request, this.response);
				smartUpload.upload();
				SmartRequest smartRequest = smartUpload.getRequest();
				Enumeration<String> parameterNames = smartRequest.getParameterNames();
				// 如果没有传参，系统自会new一个新的对象，属性没有赋值
				while (parameterNames.hasMoreElements()) {
					String tmp_name = parameterNames.nextElement();
					String value = smartRequest.getParameter(tmp_name);
					BeanOperatorUtils.setValues(tmp_name, value, currentObj);
				}
			} else {
				Enumeration<String> parameterNames = request.getParameterNames();
				// 如果没有传参，系统自会new一个新的对象，属性没有赋值
				while (parameterNames.hasMoreElements()) {
					String tmp_name = parameterNames.nextElement();
					String value = request.getParameter(tmp_name);
					BeanOperatorUtils.setValues(tmp_name, value, currentObj);
				}
			}

		} catch (IllegalArgumentException | NoSuchFieldException | SecurityException | ServletException | IOException | SmartUploadException e) {
			e.printStackTrace();
		}

	}

	/**
	 * @获取文件名称，防止重复
	 * @return
	 */
	public String createFileName(){
		SmartUpload smartUpload = SmartFactory.getInstance();
		try {
			smartUpload.initialize(super.getServletConfig(), this.request, this.response);
			smartUpload.upload();
		} catch (ServletException | IOException | SmartUploadException e) {
			e.printStackTrace();
		}
		
		return UUID.randomUUID().toString() + "." + smartUpload.getFiles().getFile(0).getFileExt();
	}
	
	
	/**
	 * @判断是否真的有文件上传
	 * @return
	 */
	public boolean isUploadFile() {
		
		try {
			if(isMuitiPart()){
				SmartUpload smartUpload = SmartFactory.getInstance();
				smartUpload.initialize(super.getServletConfig(), this.request, this.response);
				smartUpload.upload();
				return smartUpload.getFiles().getSize()>0;
			}

		} catch (IOException | ServletException | SmartUploadException e) {
			e.printStackTrace();
		}
			return false;
	}

	/**
	 * @判断是否有表单封装
	 * @return
	 */
	public boolean isMuitiPart() {

		boolean flag = false;
		String contentType = request.getContentType();
		if (StringUtils.isNotBlank(contentType)) {
			if (contentType.contains("multipart/form-data")) {
				flag = true;
			}
		}
		return flag;
	}

	/**
	 * 处理分页
	 */
	public void handleSplit(){
		
		//定义一些分页使用到的常量 只读不改不会线程安全问题
		int currentPage = 1;
		int lineSize = 5;
		String keyWord = ""; //默认为空查询全部
		// 这里因为只是查询页面会有，而列表查询不会有上传表单封装的事情
		String cp = this.request.getParameter("cp"); // 约定前端传来的就是cp
		if(StringUtils.isNotBlank(cp)){
			currentPage = Integer.parseInt(cp);
		}
		String ls = this.request.getParameter("ls");
		if(StringUtils.isNotBlank(ls)){
			lineSize = Integer.parseInt(ls);
		}
		String column = this.request.getParameter("col");
		column = StringUtils.isBlank(column) ? this.getDefaultColumn(): column;
		String kw =  request.getParameter("kw");
		if(StringUtils.isNotBlank(kw)){
			keyWord = kw;
		}
		
		this.request.setAttribute("currentPage", currentPage);
		this.request.setAttribute("lineSize", lineSize);
		this.request.setAttribute("column", column);
		this.request.setAttribute("keyWord", keyWord);
		this.request.setAttribute("columnData", this.getAllColumn());
	}
	
	/**
	 * @实现文件的上传
	 * @return
	 */
	public boolean uploadFile(String fileName){
		
		boolean flag = false;
		
		try {
			SmartUpload smartUpload = SmartFactory.getInstance();
			smartUpload.initialize(super.getServletConfig(), this.request, this.response);
			smartUpload.upload();
			String basePath = super.getServletContext().getRealPath("/"); // 拿到相对虚拟路径的实际路径
			// 取得文件上传路径 (/xxx)
			String pathDir = getUploadDir();
			String filePathDir = "";
			if(StringUtils.isBlank(pathDir)){
				filePathDir = basePath + "upload";
			}else{
				filePathDir = basePath + pathDir;
			}
			// 判断是否有该目录，如果没有就要创建 (File中可以传文件或者目录)
			File file = new File(filePathDir);
			if(!file.exists()){
				file.mkdirs();
			}
			// 再把文件名称赋值上
			String filePath = filePathDir + "/" + fileName;
			smartUpload.getFiles().getFile(0).saveAs(filePath);
			flag = true;
		} catch (ServletException | IOException | SmartUploadException e) {
			e.printStackTrace();
		}
		
		return flag;
	}
	
	
	
	// 读取访问路径的方法名称
	public String getInvokeMethodName() {
		String methodName = null;
		String uri = request.getRequestURI(); // eg: /xx/insert
		if (StringUtils.isNotBlank(uri)) {
			methodName = uri.substring(uri.lastIndexOf("/") + 1);
		}
		return methodName;
	}

	/**
	 * @设置跳转后的页面的继续路由地址和信息
	 * @param url
	 *            (配置文件的key)
	 * @param msg
	 *            (配置文件的key)
	 */
	public void setUrlAndMessage(String url, String msg) {

		// 这里注意是否要添加占位符的操作 getType() xxx.msg = {0}.ss.ss
		if (StringUtils.isNotBlank(this.getTitle())) {
			request.setAttribute("msg", MessageFormat.format(this.getMsg(msg), this.getTitle()));
		} else {
			request.setAttribute("msg", MessageFormat.format(this.getMsg(msg), ""));
		}
		request.setAttribute("url", this.getPageUrl(url));
	}

	/**
	 * @根据key获取路由信息
	 * @param key
	 * @return
	 */
	public String getPageUrl(String key) {

		return pageBundle.getString(key);
	}

	/**
	 * @根据key获取配置信息
	 * @param key
	 * @return
	 */
	public String getMsg(String key) {
		return msgBundle.getString(key);
	}

	/**
	 * @根据Key获取配置文件的验证信息
	 * @param key
	 * @return
	 */
	public String getValidationInfo(String key) {
		return validateBundle.getString(key);
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public HttpServletResponse getResponse() {
		return response;
	}
	
	public HttpSession getSession(){
		return getRequest().getSession();
	}
	
	/**
	 * 由子类实现，以供后面进行占位符的显示与填充
	 * 
	 * @return
	 */
	public abstract String getTitle();

	/**
	 * @获取所有的搜索的条件
	 * @return
	 */
	public abstract String getAllColumn();
	
	/**
	 * @获得默认的搜索条件列
	 * @return
	 */
	public abstract String getDefaultColumn();
	
	/**
	 * @获取文件上传的目录
	 * @return
	 */
	public abstract String getUploadDir();


}
